<template>
  <v-navigation-drawer
    v-model="layout.showBookmarkDrawer"
    color="background"
    floating
    location="left"
    temporary
    width="300"
  >
    <div class="d-flex flex-column h-100">
      <!-- Header: search + actions -->
      <v-sheet class="d-flex justify-space-between px-3 py-2 flex-shrink-0" elevation="1">
        <v-text-field
          ref="searchInputRef"
          v-model="search"
          :aria-label="t('drawer.search')"
          autofocus
          class="mr-2 flex-grow-1"
          clearable
          density="compact"
          hide-details
          inputmode="search"
          :placeholder="t('drawer.search')"
          :prepend-inner-icon="mdiMagnify"
          variant="underlined"
        />
        <div class="d-flex align-center">
          <v-btn
            :aria-label="t('drawer.manage')"
            :icon="mdiCog"
            :title="t('drawer.manage')"
            variant="text"
            @click="openFullManager"
          />
        </div>
      </v-sheet>

      <!-- Scrollable content area -->
      <div ref="listContainerRef" class="flex-grow-1 bookmark-drawer-main" @dragover.prevent>
        <!-- Empty state -->
        <v-list
          v-if="filteredFolderTree.length === 0"
          aria-live="polite"
          density="compact"
          role="status"
          slim
        >
          <v-list-item>
            <v-list-item-title class="text-medium-emphasis text-center">
              {{ t('drawer.empty') }}
            </v-list-item-title>
          </v-list-item>
        </v-list>

        <!-- Virtual scroll for better performance with large lists -->
        <v-virtual-scroll
          v-else
          class="bookmark-virtual-scroll"
          :item-height="40"
          :items="filteredFolderTree"
        >
          <template #default="{ item }">
            <folder-item
              v-if="'hasChildren' in item"
              :key="(item as any)._id"
              :node="item as FolderNode"
              @enter="enterMenu"
              @ondrop="onDrop"
              @opened="close"
            />
            <book-item
              v-else
              :key="(item as BookmarkDTO)._id"
              :node="item as BookmarkDTO"
              @mouseenter="enterItem"
              @ondrop="onDrop"
              @opened="close"
            />
          </template>
        </v-virtual-scroll>
      </div>
    </div>
    <template v-for="(n, i) of menu_list" :key="n.node._id">
      <BookmarkList
        :index="i"
        :node="n.node"
        :pos="{ x: n.x, y: n.y }"
        @enter="enterMenu($event, false)"
        @leave="leaveMenu($event)"
        @ondrop="onDrop"
        @opened="close"
      />
    </template>
  </v-navigation-drawer>
</template>

<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLayoutStore } from '@/windows/layoutBrowser/stores/layout'
import {
  type BookmarkDTO,
  type BookmarkFolderDTO,
  type FolderNode,
  useBookmarkManagerStore
} from '@/stores/bookmarkManager'
import { getSystemTab } from '~shared/utils/tab'
import { TAB_IDS } from '~shared/constants'
import { mdiCog, mdiMagnify } from '@mdi/js'
import debounce from 'lodash/debounce'
import delay from 'lodash/delay'
import { useTabStore } from '@/windows/layoutBrowser/stores/tab'

const { t } = useI18n()
const layout = useLayoutStore()
const bm = useBookmarkManagerStore()
const tabStore = useTabStore()

const menu_list = ref<{ node: FolderNode; x: number; y: number }[]>([])

const listContainerRef = ref<HTMLElement | null>(null)
const lastPos = ref<{ node: FolderNode; x: number; y: number; root?: boolean } | null>(null)

const search = ref('')
const debouncedSearch = ref('')
const searchInputRef = ref<any>(null)
const updateDebouncedSearch = debounce((v: string) => {
  debouncedSearch.value = v
}, 200)
watch(search, v => updateDebouncedSearch(v), { immediate: true })

const items = computed(() => bm.items as BookmarkDTO[])
const folders = computed(() => bm.folders as BookmarkFolderDTO[])

function close() {
  layout.showBookmarkDrawer = false
  menu_list.value = []
}

// 使用 Map 预索引，避免重复遍历
const folderTree = computed(() => {
  // 1. 构建索引 - 使用 string | null 作为 key 类型
  const foldersByParent = new Map<string | null, BookmarkFolderDTO[]>()
  const bookmarksByFolder = new Map<string | null, BookmarkDTO[]>()

  folders.value.forEach(f => {
    const key = f.parent_id ?? null
    if (!foldersByParent.has(key)) foldersByParent.set(key, [])
    foldersByParent.get(key)!.push(f)
  })

  items.value.forEach(b => {
    const key = b.folder_id ?? null
    if (!bookmarksByFolder.has(key)) bookmarksByFolder.set(key, [])
    bookmarksByFolder.get(key)!.push(b)
  })

  // 2. 递归构建树（使用索引，O(n)）
  function build(parentId: string | null): FolderNode[] {
    const children = foldersByParent.get(parentId) || []
    return children
      .sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0))
      .map(folder => ({
        _id: folder._id,
        name: folder.name,
        children: build(folder._id),
        bookmarks: (bookmarksByFolder.get(folder._id) || []).sort(
          (a, b) => (a.sort ?? 0) - (b.sort ?? 0)
        ),
        sort: folder.sort ?? 0,
        hasChildren: foldersByParent.has(String(folder._id))
      }))
  }

  // 3. 合并未分类书签（使用归并算法，O(n) 而不是 O(n log n)）
  const tree = build(null)
  const unfiled = (bookmarksByFolder.get(null) || []).sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0))

  // 如果没有未分类书签，直接返回树
  if (unfiled.length === 0) return tree

  // 如果没有文件夹，直接返回未分类书签
  if (tree.length === 0) return unfiled

  // 归并两个已排序的数组（O(n)）
  const result: (FolderNode | BookmarkDTO)[] = []
  let i = 0,
    j = 0

  while (i < unfiled.length && j < tree.length) {
    const unfiledItem = unfiled[i]!
    const treeItem = tree[j]!
    const unfiledOrder = unfiledItem.sort ?? 0
    const treeOrder = treeItem.sort ?? 0

    if (unfiledOrder <= treeOrder) {
      result.push(unfiledItem)
      i++
    } else {
      result.push(treeItem)
      j++
    }
  }

  // 添加剩余元素
  while (i < unfiled.length) {
    result.push(unfiled[i]!)
    i++
  }
  while (j < tree.length) {
    result.push(tree[j]!)
    j++
  }

  return result
})

// Filter folder tree based on search query (debounced)
const filteredFolderTree = computed<(FolderNode | BookmarkDTO)[]>(() => {
  const kw = debouncedSearch.value.trim().toLowerCase()
  if (!kw) return folderTree.value

  // Filter function that keeps folders with matching bookmarks or subfolders
  const filterNode = (node: FolderNode | BookmarkDTO): FolderNode | BookmarkDTO | null => {
    let matchingBookmarks: BookmarkDTO[] = []
    if ('bookmarks' in node) {
      matchingBookmarks = node.bookmarks.filter(
        b => (b.title || '').toLowerCase().includes(kw) || (b.link || '').toLowerCase().includes(kw)
      )
    }
    let filteredChildren: FolderNode[] = []
    if ('children' in node) {
      filteredChildren = node.children.map(filterNode).filter(Boolean) as FolderNode[]
    }

    // Keep node if it has matching bookmarks or matching children
    if (matchingBookmarks.length > 0 || filteredChildren.length > 0) {
      return {
        ...(node as FolderNode),
        bookmarks: matchingBookmarks,
        children: filteredChildren,
        hasChildren: filteredChildren.length > 0
      }
    } else if (
      'title' in node &&
      'url' in node &&
      ((node.title || '').toLowerCase().includes(kw) ||
        (node.link || '').toLowerCase().includes(kw))
    ) {
      return node
    }
    return null
  }

  return folderTree.value.map(filterNode).filter(Boolean) as (FolderNode | BookmarkDTO)[]
})

function onDrop(data: { source: FolderNode | BookmarkDTO; target: FolderNode | BookmarkDTO }) {
  // TODO: 实现拖拽排序需要在子项组件透传拖拽数据
  console.log('drop', data)
}

/**
 * 鼠标离开文件夹项时，从菜单ID列表中移除
 */
function leaveMenu(node: FolderNode) {
  menu_list.value = menu_list.value.filter(n => n.node._id !== node._id)
}

function enterItem() {
  lastPos.value = null
  delay(() => {
    menu_list.value = []
  }, 500)
}

const debouncedShowMenu = debounce(showMenu, 500, { trailing: true })

function showMenu() {
  if (lastPos.value) {
    if (lastPos.value.root) {
      menu_list.value = []
    }
    const exists = menu_list.value.find(n => n.node._id === lastPos.value?.node._id)
    if (!exists) menu_list.value.push(lastPos.value)
  }
}

/**
 * 根据鼠标位置显示子菜单
 * 当鼠标进入文件夹项时，将其ID添加到菜单ID列表中
 */
async function enterMenu(data: { node: FolderNode; event: MouseEvent }, root = true) {
  const { node, event } = data
  // 如果文件夹没有子项（书签或子文件夹），不显示菜单
  if (!node.hasChildren && node.bookmarks.length === 0) {
    return
  }
  const rect = (event.target as HTMLElement).getBoundingClientRect()
  lastPos.value = { node, x: rect.left, y: rect.top, root }
  debouncedShowMenu()
}

function openFullManager() {
  // Open full bookmark page tab via system tab BOOKMARKS
  const tab = getSystemTab(TAB_IDS.BOOKMARKS)
  _pre_.browser.addTab(tab, 'right')
  close()
}

onMounted(async () => {
  await bm.loadBookmarks()
  await bm.loadFolders()
  // console.log('folderTree', folderTree.value, bm.folders, bm.items)
})
watch(
  () => layout.showBookmarkDrawer,
  v => {
    if (!v) {
      menu_list.value = []
    }
  }
)
watch(
  () => tabStore.activeTab,
  v => {
    if (v) {
    }
  }
)
onBeforeUnmount(() => {
  debouncedShowMenu.cancel()
  updateDebouncedSearch.cancel()
})
</script>

<i18n>
{
  "zhHans": {
    "drawer": {
      "search": "搜索书签...",
      "add": "收藏",
      "addCurrent": "收藏当前页面",
      "manage": "管理",
      "all": "全部",
      "openNew": "在新标签打开",
      "edit": "编辑",
      "delete": "删除",
      "empty": "无结果",
      "unfiled": "未分类"
    }
  },
  "en": {
    "drawer": {
      "search": "Search bookmarks...",
      "add": "Add",
      "addCurrent": "Add current page",
      "manage": "Manage",
      "all": "All",
      "openNew": "Open in new tab",
      "edit": "Edit",
      "delete": "Delete",
      "empty": "No results",
      "unfiled": "Unfiled"
    }
  }
}
</i18n>

<style scoped>
.bookmark-drawer-main {
  height: 100%;
  overflow: hidden;
  position: relative;
}

.bookmark-virtual-scroll {
  height: 100%;
  width: 100%;
}

/* 确保虚拟滚动容器内的项目样式正确 */
.bookmark-virtual-scroll :deep(.v-virtual-scroll__container) {
  width: 100%;
}
</style>
